package com.wismay.gc.service.dashboard;

import java.io.File;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import com.wismay.gc.core.datamode.Class;
import com.wismay.gc.core.datamode.DataMode;
import com.wismay.gc.core.datamode.Field;
import com.wismay.gc.core.datasource.Datasource;
import com.wismay.gc.core.datasource.DbConnInfo;
import com.wismay.gc.core.datasource.impl.DatasourceForMysqlImpl;
import com.wismay.gc.core.transform.JavaNameing;
import com.wismay.gc.core.transform.impl.JavaTransformer;
import com.wismay.gc.elfinder.util.PropertiesUtil;
import com.wismay.gc.entity.DataSource;
import com.wismay.gc.repository.DataSourceDao;
import com.wismay.gc.util.FileUtil;

import freemarker.template.Configuration;
import freemarker.template.DefaultObjectWrapper;
import freemarker.template.Template;
import freemarker.template.TemplateException;
import freemarker.template.TemplateExceptionHandler;

//Spring Bean的标识.
@Component
// 默认将类中的所有public函数纳入事务管理.
@Transactional
public class DataSourceService {
	private static Logger logger = LoggerFactory.getLogger(DataSourceService.class);
	private static final String DRIVERS = "drivers";
	private static final String ROOT_DIR = PropertiesUtil.getProperty("TemplateAndCode.rootDir");

	@Autowired
	private DataSourceDao dataSourceDao;

	public DataSource findOne(Long id) {
		return dataSourceDao.findOne(id);
	}

	public void save(DataSource entity) {
		dataSourceDao.save(entity);
	}

	public void delete(Long id) {
		dataSourceDao.delete(id);
	}

	public List<DataSource> findAll() {
		return (List<DataSource>) dataSourceDao.findAll();
	}

	public DataSource findByProjectId(Long projectId) {
		// TODO Auto-generated method stub
		return dataSourceDao.findByProjectId(projectId);
	}

	public void saveDriverFile(Long projectId, String driverFileName, String ctxDir, MultipartFile driverFile)
			throws Exception {
		saveDriverFile(ctxDir, driverFile);
		DataSource dataSource = dataSourceDao.findByProjectId(projectId);
		dataSource.setDriverFileName(driverFileName);
		dataSourceDao.save(dataSource);
	}

	private void saveDriverFile(String ctxDir, MultipartFile driverFile) throws Exception {
		String driverSavePath = ctxDir + File.separator + DRIVERS + File.separator + driverFile.getOriginalFilename();
		logger.info("driverSavePath:" + driverSavePath);
		File file = new File(driverSavePath);
		FileOutputStream fos = new FileOutputStream(file);
		try {
			fos.write(driverFile.getBytes());
			fos.flush();
		} catch (Exception e) {
			throw e;
		} finally {
			fos.close();
		}

	}

	/**
	 * 生成代码
	 * 
	 * @param projectId
	 * @param ctxDir
	 * @throws Exception
	 */
	public void genCode(Long projectId, String ctxDir) throws Exception {
		// 查找所有ftl模板文件
		List<File> ftlTemplates = new ArrayList<File>();
		String templateRootDir = ROOT_DIR + File.separator + projectId;
		String targetFileName = "*.ftl";
		FileUtil.findFiles(templateRootDir, targetFileName, ftlTemplates);
		if (ftlTemplates.isEmpty()) {
			throw new Exception("缺失代码模板");
		}

		// 从数据库读取元数据
		DataSource dataSource = dataSourceDao.findByProjectId(projectId);
		Datasource dmda = new DatasourceForMysqlImpl(getDbConnInfo(dataSource));

		// 需要移除的字符串
		List<String> removeStr = new ArrayList<String>();
		removeStr.add("t_");

		// 元数据转换成数据模型
		DataMode dataMode = JavaTransformer.transform(dmda.getMetaMode(), removeStr);

		// 合并用户数据（暂未实现用户自定义数据模型）

		// 生成源码：模板+数据模型=源代码
		genCode(ftlTemplates, dataMode);

	}

	/**
	 * 生成源码：模板+数据模型=源代码
	 * 
	 * @param ftlTemplates
	 * @param dataMode
	 * @throws IOException
	 * @throws TemplateException
	 */
	private void genCode(List<File> ftlTemplates, DataMode dataMode) throws Exception {
		System.out.println("=============模板=============");
		for (File f : ftlTemplates) {
			System.out.println(f);
		}

		System.out.println("=============数据模型=============");
		for (com.wismay.gc.core.datamode.Class c : dataMode.getClasses()) {
			System.out.println(c.getName() + "[" + c.getComment() + "]" + "   //" + c.getTableName());
			for (Field f : c.getFields()) {
				System.out.println("    " + f.getName() + "  " + f.getType() + " " + "[" + f.getComment() + "]   //"
						+ f.getColumnName() + "  //" + f.getNameForPascal());
			}
		}

		/**
		 * freemarker
		 */

		Configuration cfg = new Configuration();
		cfg.setObjectWrapper(new DefaultObjectWrapper());
		cfg.setDefaultEncoding("UTF-8");
		cfg.setTemplateExceptionHandler(TemplateExceptionHandler.HTML_DEBUG_HANDLER);
		for (Class c : dataMode.getClasses()) {
			for (File f : ftlTemplates) {
				cfg.setDirectoryForTemplateLoading(new File(f.getParent()));

				/* Create a data-model */
				Map<String, Object> root = new HashMap<String, Object>();
				root.put("class", c);
				root.put("userData", dataMode.getUserData());

				/* Get the template */
				Template temp = cfg.getTemplate(f.getName());

				String codeFilePathname=f.getParent()+File.separator+procSrcFileName(f.getName(),c.getName());
				File codeFile = new File(codeFilePathname);
				/* Merge data-model with template */
				Writer out = new FileWriter(codeFile);
				temp.process(root, out);
			}
		}

	}

	/**
	 * 生成java类的文件名，去掉ftl后缀，并加上类名。 例如：
	 * <ul>
	 * <li>.java.ftl ==> ClassName.java</li>
	 * <li>service.java.ftl ==> ClassNameService.java</li>
	 * <li>dao.java.ftl ==> ClassNameDao.java</li>
	 * </ul>
	 * 
	 * @param ftlFileName
	 *            ftl模板名称
	 * @param className
	 *            类名
	 * @return java类的文件名
	 * @throws Exception 
	 */
	public static String procSrcFileName(String ftlFileName, String className) throws Exception {
		// service.java.ftl
		String tmpFileName = "";
		int lastDotIndex = ftlFileName.lastIndexOf(".");

		// service.java
		tmpFileName = ftlFileName.substring(0, lastDotIndex);

		int endDotIndex = tmpFileName.lastIndexOf(".");
		if (endDotIndex != -1) {
			String suffix = tmpFileName.substring(endDotIndex + 1);// suffix="java"
			String perffix = tmpFileName.substring(0, endDotIndex);// suffix="service"

			String srcFileName = JavaNameing.toCamel(className) + JavaNameing.toCamel(perffix) + "." + suffix;

			return srcFileName;
		}else{
			throw new Exception(ftlFileName+" 模板命名不符合规范：[自定义名称].<源码后缀>.ftl  例如：service.java.ftl");
		}
	}

	public static void main(String[] args) throws Exception {
		System.out.println(procSrcFileName(".java.ftl", "User"));

	}

	/**
	 * 数据库连接测试
	 * 
	 * @param projectId
	 *            2013-9-16 Petter
	 * @throws Exception
	 */
	public void testMysqlConnect(Long projectId, String ctxDir) throws Exception {
		DataSource dataSource = dataSourceDao.findByProjectId(projectId);

		Datasource ds = new DatasourceForMysqlImpl(getDbConnInfo(dataSource));
		ds.testConn();
	}

	private DbConnInfo getDbConnInfo(DataSource ds) {
		DbConnInfo dbInfo = new DbConnInfo(ds.getDriver(), ds.getUrl(), ds.getUsername(), ds.getPassword(),
				ds.getDriverFileName());
		return dbInfo;
	}

}
